<template>
  <!-- 内容区 -->
  <div class="history-curve">
    <div class="tools">
      <el-popover trigger="hover" placement="bottom" :width="180" :teleported="false">
        <el-checkbox-group
          class="sensor-type-checkbox"
          v-model="sensorTypes"
          @change="handleSensorTypeChange"
        >
          <el-checkbox
            v-for="item in sensorTypeList"
            :key="item.id"
            :label="item.label"
            :value="item.id"
          />
        </el-checkbox-group>
        <template #reference>
          <el-button :icon="Filter" />
        </template>
      </el-popover>
      <el-input
        v-model="filterVal"
        class="sensor-input"
        placeholder="请输入关键字进行过滤"
        type="text"
      />
      <el-date-picker
        class="time-picker"
        v-model="searchData.timeRange"
        start-placeholder="开始时间"
        end-placeholder="结束时间"
        value-format="YYYY-MM-DD HH:mm:ss"
        type="datetimerange"
      />
    </div>

    <div class="content">
      <div class="left">
        <el-checkbox-group class="my-checkbox" v-model="searchData.checkedCdCode">
          <el-checkbox
            v-for="item in filterSensorList"
            :key="item.cdCode"
            :value="item.cdCode"
            :label="item.areaLocation"
          />
        </el-checkbox-group>
      </div>
      <!-- 右边图表区 -->
      <BEchart class="charts" :options="chartOption"></BEchart>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
import { getHisGraph, getSensorDict } from '@/api/sensor'
import { debounce } from 'lodash'
import BEchart from '@/components/b-echart/index.vue'
import { useAuthStore } from '@/store/auth'
import { storeToRefs } from 'pinia'
import { Filter } from '@element-plus/icons-vue'
import { getMineralDictList } from '@/api/dict'

interface ChartDataItem {
  dateMinute: string
  cdValue: number
  formatter: string
  cdState?: string
  cdStateName?: string
  showValue?: string
  unitCode?: string
  cdLocation?: string
}

interface ChartData {
  cdCode: string
  cdLocation: string
  data: ChartDataItem[]
  sensorType?: string
  maxValue?: number
  minValue?: number
  averageValue?: number
  unitCode?: string
  bjIndexs?: Array<[number, number]>
  bxIndexs?: Array<[number, number]>
}

interface SearchData {
  timeRange: [string, string]
  checkedCdCode: string[]
}

interface AreaLocation {
  cdCode: string
  areaLocation: string
}

interface LegendData {
  name: string
  maxValue?: number
  minValue?: number
  averageValue?: number
  unitCode?: string
}

interface ChartOption {
  color: string[]
  title: {
    text: string
    left: string
    textStyle: {
      color: string
    }
  }
  tooltip: {
    trigger: string
    formatter: (params: any) => string
  }
  legend: {
    top: string
    type: string
    left: string
    data: LegendData[]
    textStyle: {
      color: string
      fontSize: number
    }
    formatter?: (name: string) => string
  }
  grid: {
    top: string
    left: string
    right: string
    bottom: string
  }
  xAxis: {
    type: string
    data: string[]
    axisLine: {
      lineStyle: {
        color: string
      }
    }
  }
  yAxis: {
    type: string
    name: string
    axisLine: {
      lineStyle: {
        color: string
      }
    }
  }
  series: any[]
  visualMap: any[]
}

const props = defineProps({
  params: {
    type: Object,
    default: () => ({})
  }
})

const loading = ref(false)
const filterVal = ref('')
const sensorList = ref<AreaLocation[]>([])
const isMore24Hour = ref(false)
const eChartDataCache = ref<ChartData[]>([])
const curve_patching_duration_config = ref(60) // Default 60 seconds

const searchData = ref<SearchData>({
  timeRange: ['', ''],
  checkedCdCode: []
})

const pickerOptions = {
  disabledDate: (time: Date) => {
    const time1 = dayjs().format('YYYY-MM-DD 23:59:59')
    return time.getTime() > new Date(time1).getTime()
  }
}

const chartOption = ref<ChartOption>({
  color: [
    '#0EECF1',
    '#ffff00',
    '#ffa500',
    '#0000FF',
    '#C43CC4',
    '#9400d3',
    '#9f79ee',
    '#696969',
    '#ff00ff',
    '#bdb76b',
    '#cd5c5c',
    '#006400',
    '#00bfff',
    '#66cd00',
    '#8b8989',
    '#ff1493'
  ],
  title: {
    text: '',
    left: 'center',
    textStyle: {
      color: '#fff'
    }
  },
  dataZoom: [
    {
      type: 'slider',
      show: true,
      bottom: 20,
      dataBackground: {
        areaStyle: {
          color: '#333',
          opacity: 0.3
        }
      }
    },
    {
      type: 'inside'
    },
    {
      type: 'slider',
      show: true,
      yAxisIndex: 0,
      filterMode: 'empty',
      height: '70%',
      showDataShadow: false,
      right: 15
    }
  ],
  tooltip: {
    trigger: 'axis',
    formatter: (params: any) => {
      let result = ''
      if (Array.isArray(params)) {
        params.forEach((item: any, index: number) => {
          if (index === 0) {
            result += item.data.dateMinute + '<br>'
          }
          result += item.marker + item.data.formatter + '<br>'
        })
      }
      return result
    }
  },
  legend: {
    top: '8%',
    type: 'plain',
    left: 'center',
    data: [],
    textStyle: {
      color: '#fff',
      fontSize: 12
    }
  },
  grid: {
    top: '20%',
    left: '8%',
    right: '60',
    bottom: '13%'
  },
  xAxis: {
    type: 'category',
    data: [],
    axisLine: {
      lineStyle: {
        color: '#fff'
      }
    }
  },
  yAxis: {
    type: 'value',
    name: '监测值',
    axisLine: {
      lineStyle: {
        color: '#fff'
      }
    }
  },
  series: [],
  visualMap: []
})

const filterSensorList = computed(() => {
  return sensorList.value.filter((item) =>
    filterVal.value ? item.areaLocation.includes(filterVal.value) : true
  )
})

const clearECharts = () => {
  if (chartOption.value.xAxis && 'data' in chartOption.value.xAxis) {
    chartOption.value.xAxis.data = []
  }
  if (chartOption.value.legend) {
    chartOption.value.legend.data = []
  }
  chartOption.value.series = []
  chartOption.value.visualMap = []
}

const formatDate = (timestamp: number, format: string): string => {
  return dayjs(timestamp).format(format)
}

const isNotNull = (value: any): boolean => {
  return value !== null && value !== undefined
}

const getString = (value: any): string => {
  return value?.toString() || ''
}

const getHisGraphFun = debounce(async () => {
  if (!searchData.value.checkedCdCode.length) {
    ElMessage.error('请选择传感器')
    clearECharts()
    return
  }

  if (!searchData.value.timeRange.length) {
    ElMessage.error('请选择开始时间和结束时间')
    clearECharts()
    return
  }

  loading.value = true

  try {
    const { data } = await getHisGraph({
      cdCode: props.params.cdCode,
      cdLocation: props.params.cdLocation,
      cdCodes: searchData.value.checkedCdCode,
      startTime: searchData.value.timeRange[0],
      endTime: searchData.value.timeRange[1]
    })
    eChartDataCache.value = data.data
    isMore24Hour.value = data.isMore24Hour
    showEChartData()
  } catch (error) {
    clearECharts()
  } finally {
    loading.value = false
  }
}, 500)

const showEChartData = () => {
  clearECharts()

  // 生成提示信息的函数
  const formatter = (data: ChartDataItem): string => {
    let s = data.cdLocation || ''

    if (isNotNull(data.cdValue)) {
      s += ' ' + data.showValue
    }

    // 不正常
    if (getString(data.cdState) !== '0') {
      s += ' ' + getString(data.cdStateName)
    }

    return s
  }

  // 生成报警或标校数据的函数
  const piecesFun = (pieces: any[], data: Array<[number, number]>, color: string) => {
    data.forEach((item) => {
      const itemLength = item.length
      if (itemLength === 1) {
        pieces.push({
          gte: item[0],
          lte: item[0],
          color: color
        })
      } else if (itemLength > 1) {
        pieces.push({
          gte: item[0],
          lte: item[1],
          color: color
        })
      }
    })
  }

  // 需要先 初始化 series legend 构建 X 轴
  const obj: Record<string, number> = {} // 用来去重的对象
  for (let i = 0; i < eChartDataCache.value.length; i++) {
    const { data, sensorType, cdLocation, maxValue, minValue, averageValue, unitCode } =
      eChartDataCache.value[i]
    // 开关的特殊处理
    let step: boolean | 'start' = false
    chartOption.value.legend.data.push({
      name: cdLocation,
      maxValue,
      minValue,
      averageValue,
      unitCode
    })

    if (sensorType && sensorType.substr(0, 1) !== '0') {
      step = 'start'
    } else {
      chartOption.value.legend.formatter = (name: string) => {
        let legend = name
        for (const data of chartOption.value.legend.data) {
          if (data.name === name) {
            if (data.maxValue || data.maxValue === 0) {
              legend += `  最大值：${data.maxValue}${data.unitCode}`
            }
            if (data.minValue || data.minValue === 0) {
              legend += `  最小值：${data.minValue}${data.unitCode}`
            }
            if (data.averageValue || data.averageValue === 0) {
              legend += `  平均值：${data.averageValue}${data.unitCode}`
            }
          }
        }
        return legend
      }
    }

    // 增加tag和初始化series
    chartOption.value.series.push({
      type: 'line',
      name: cdLocation,
      data: [],
      lineStyle: {
        width: 3
      },
      step
    })

    // 构建 X 轴 合并去重所有传感器的时间
    for (const item of data) {
      const { dateMinute } = item
      if (!obj[dateMinute]) {
        chartOption.value.xAxis.data.push(dateMinute)
        obj[dateMinute] = 1
      }
    }
  }

  // 对X轴排序
  chartOption.value.xAxis.data.sort((a: string, b: string) => {
    return new Date(a).getTime() - new Date(b).getTime()
  })

  //补点间隔
  let interval = curve_patching_duration_config.value * 1000 || 60000
  if (isMore24Hour.value) {
    interval = Infinity
  }

  // 首尾与时间控件不匹配时添加首尾点为时间控件值
  const startTime = new Date(searchData.value.timeRange[0]).getTime()
  const startXTime = new Date(chartOption.value.xAxis.data[0]).getTime()
  if (startXTime - startTime > interval) {
    chartOption.value.xAxis.data.unshift(searchData.value.timeRange[0])
  }

  const endTime = new Date(searchData.value.timeRange[1]).getTime()
  const endXTime = new Date(
    chartOption.value.xAxis.data[chartOption.value.xAxis.data.length - 1]
  ).getTime()
  if (endTime - endXTime > interval) {
    chartOption.value.xAxis.data.push(searchData.value.timeRange[1])
  }

  // 循环X轴镜像防止索引错位
  const oldXAxis = JSON.parse(JSON.stringify(chartOption.value.xAxis.data))
  // 记录补点的个数
  let j = 0
  for (let x = 0; x < oldXAxis.length; x++) {
    const time = oldXAxis[x]
    const nextNinute = oldXAxis[x + 1]
    const currentStartTime = new Date(time).getTime()
    const currentEndTime = new Date(nextNinute).getTime()
    // 比较当前节点和下一个节点的时间间隔  间隔大于interval分钟补一个时间
    if (nextNinute && currentEndTime - currentStartTime > interval) {
      const len = Math.floor((currentEndTime - currentStartTime) / interval)
      // 一共间隔了 len 分钟 向下取整
      for (let i = 0; i < len; i++) {
        const dot = formatDate(currentStartTime + interval * (i + 1), 'yyyy-MM-dd HH:mm:ss')
        // 在X轴中插入时间 应为 镜像的下标 + 1 + 之前补点的数量
        chartOption.value.xAxis.data.splice(x + 1 + j, 0, dot)
        j++
      }
    }
  }

  // 通过新的X轴补点 获取传感器在新X轴上的标校和报警索引
  for (let i = 0; i < eChartDataCache.value.length; i++) {
    // 获取需要的传感器参数
    const { data, bjIndexs, bxIndexs, cdLocation } = eChartDataCache.value[i]
    // 渲染报警和标校的颜色 源数据返回的是当前传感器索引 需要通过索引获取时间 再通过时间去在合并后的 x 轴获取到新的索引
    if ((bjIndexs && bjIndexs.length) || (bxIndexs && bxIndexs.length)) {
      const pieces: any[] = []

      if (bjIndexs && bjIndexs.length) {
        const bjValues = bjIndexs.map((scope: [number, number]) => {
          const arr = [data[scope[0]].dateMinute]
          if (scope[1] !== undefined) {
            arr.push(data[scope[1]].dateMinute)
          }
          return arr
        })
        const newbjIndexs = bjValues.map((scope: string[]) => {
          return [
            chartOption.value.xAxis.data.findIndex((time: string) => time === scope[0]),
            scope[1]
              ? chartOption.value.xAxis.data.findIndex((time: string) => time === scope[1])
              : undefined
          ].filter((index): index is number => index !== undefined) as [number, number]
        })
        piecesFun(pieces, newbjIndexs, '#EF585C')
      }

      if (bxIndexs && bxIndexs.length) {
        const bxValues = bxIndexs.map((scope: [number, number]) => {
          const arr = [data[scope[0]].dateMinute]
          if (scope[1] !== undefined) {
            arr.push(data[scope[1]].dateMinute)
          }
          return arr
        })
        const newbxIndexs = bxValues.map((scope: string[]) => {
          return [
            chartOption.value.xAxis.data.findIndex((time: string) => time === scope[0]),
            scope[1]
              ? chartOption.value.xAxis.data.findIndex((time: string) => time === scope[1])
              : undefined
          ].filter((index): index is number => index !== undefined) as [number, number]
        })
        piecesFun(pieces, newbxIndexs, '#73EA5E')
      }

      chartOption.value.visualMap.push({
        show: false,
        dimension: 0,
        seriesIndex: i,
        pieces,
        outOfRange: {
          color: chartOption.value.color[i % 15],
          symbolSize: [0]
        }
      })
    }

    // 通过合并后的时间轴生成数据
    for (let x = 0; x < chartOption.value.xAxis.data.length; x++) {
      const time = chartOption.value.xAxis.data[x]
      // 按照x轴顺序找到源数据中当前时间的点位
      const currentDot = data.find((item: ChartDataItem) => item.dateMinute === time)
      // 如果存在就加入series
      if (currentDot) {
        chartOption.value.series[i].data.push({
          value: currentDot.cdValue,
          dateMinute: time,
          formatter: formatter(currentDot)
        })
      } else {
        // 不存在就需要补一个空点
        chartOption.value.series[i].data.push({
          value: null,
          dateMinute: time,
          formatter: cdLocation
        })
      }
    }
  }
}

const authStore = useAuthStore()
const { globalParams } = storeToRefs(authStore)

const sensorTypeList = ref<string[]>([])
function handleSensorTypeChange(val: string[]) {
  getSensorDict(val).then((res) => {
    sensorList.value = res.data
  })
}

const sensorTypes = ref<string[]>([])
onMounted(() => {
  sensorTypes.value.push(props.params.sensorType)
  getMineralDictList().then((res) => {
    sensorTypeList.value = res.data
  })
  getSensorDict(sensorTypes.value).then((res) => {
    sensorList.value = res.data
  })
  if (props.params.beginDate) {
    searchData.value.timeRange = [props.params.beginDate, props.params.endDate]
  } else {
    searchData.value.timeRange = [
      dayjs(globalParams.value.startTime).format('YYYY-MM-DD HH:mm:ss'),
      dayjs(globalParams.value.endTime).format('YYYY-MM-DD HH:mm:ss')
    ]
  }
  searchData.value.checkedCdCode = [props.params.cdCode]
  getHisGraphFun()

  watch(
    searchData.value,
    () => {
      getHisGraphFun()
    },
    {
      deep: true
    }
  )
})
</script>

<style lang="scss" scoped>
.history-curve {
  display: flex;
  flex-direction: column;
  height: 100%;
  .tools {
    display: flex;
    align-items: center;
    .sensor-type-checkbox {
      height: 240px;
      overflow-y: auto;
      flex-direction: column;
    }
    .sensor-input {
      width: 180px;
      margin: 0 8px 0 4px;
    }
    .time-picker {
      flex: 1;
      max-width: 300px;
    }
  }
  .content {
    display: flex;
    flex: 1;
    .left {
      margin-top: 8px;
      width: 230px;
      height: calc(100% - 8px);
      margin-right: 8px;
      flex-shrink: 0;
      overflow-y: auto;
      .my-checkbox {
        flex-direction: column;
      }
    }
    .charts {
      flex: 1;
    }
  }
}
</style>
